home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / STREAM13.ARJ / STREAMS.PAS < prev    next >
Pascal/Delphi Source File  |  1992-05-18  |  68KB  |  2,363 lines

  1. {$B-}   { Use fast boolean evaluation. }
  2.  
  3. unit Streams;
  4.  
  5. { Unit to provide enhancements to TV Objects unit streams in the form
  6.   of several filters, i.e. stream clients, and other streams. }
  7.  
  8. { Version 1.2 - Adds TNulStream and TXMSStream, from suggestion and
  9.                 code by Stefan Boether; TBitFilter, from suggestion
  10.                 by Rene Seguin; added call to Flush to TFilter.Done;
  11.                 UseBuf and OwnMem to TRAMStream.
  12.                 TTextFilter fixed so that mixed access methods work.
  13.           1.3 - Added TDupFilter, TSequential, CRCs and Checksums }
  14.  
  15. {$ifndef windows}
  16.   {$O-}
  17.   { Don't overlay this unit; it contains code that needs to participate
  18.          in overlay management. }
  19. {$endif
  20.  
  21. {  Hierarchy:
  22.  
  23.    TStream                  (from Objects)
  24.      TFilter                Base type for filters
  25.        TEncryptFilter       Encrypts as it writes; decrypts as it reads
  26.        TLZWFilter           Compresses as it writes; expands as it reads
  27.        TTextFilter          Provides text file interface to stream
  28.        TLogFilter           Provides logging of text file activity
  29.        TBitFilter           Allows reads & writes by the bit
  30.        TDupFilter           Duplicates output, checks for matching input
  31.        TSequential          Filter that doesn't allow Seek
  32.          TChksumFilter      Calculates 16 bit checksum for reads and writes
  33.          TCRC16Filter       Calculates XMODEM-style 16 bit CRC
  34.          TCRCARCFilter      Calculates ARC-style 16 bit CRC
  35.          TCRC32Filter       Calculates ZIP/ZModem-style 32 bit CRC
  36.      TNulStream             Eats writes, returns constant on reads
  37.      TRAMStream             Stream in memory
  38.      TXMSStream             Stream in XMS
  39.      TDOSStream             (from Objects)
  40.        TBufStream           (from Objects)
  41.          TNamedBufStream    Buffered file stream that knows its name
  42.            TTempBufStream   Buffered file stream that erases itself when done
  43.  
  44.    Procedures & functions:
  45.  
  46.    TempStream      allocates a temporary stream
  47.    OvrInitStream   like OvrInitEMS, but buffers overlays on a stream
  48.                    May be called several times to buffer different
  49.                    segments on different streams.
  50.    OvrDetachStream detaches stream from overlay system
  51.    OvrDisposeStreams detaches all streams from overlay system and disposes of
  52.                    them
  53.    OvrSizeNeeded   Calculates the size needed to load the rest of the segments
  54.                    to a stream
  55.    OvrLoadAll      immediately copies as many overlay segments to the stream
  56.                    as will fit
  57.    UpdateChkSum    updates a 16 bit checksum value
  58.    UpdateCRC16     updates a CRC16 value
  59.    UpdateCRCARC    updates a CRCARC value
  60.    UpdateCRC32     updates a CRC32 value
  61.  
  62. }
  63.  
  64. interface
  65.  
  66. {$ifdef windows}
  67. uses strings,windos,winprocs,wobjects;
  68. {$else}
  69. uses DOS, Overlay, Objects;
  70. {$endif}
  71.  
  72. const
  73.   stBadMode = 1;                  { Bad mode for stream - operation not supported
  74.                                     info = mode }
  75.   stStreamFail = 2;               { Stream init failed }
  76.   stBaseError = 3;                { Error in base stream
  77.                                     info = base error value }
  78.   stMemError = 4;                 { Not enough memory for operation }
  79.   stSigError = 5;                 { Problem with LZ file signature }
  80.   stUsedAll = 6;                  { Used limit of allocation }
  81.   stUnsupported = 7;              { Operation unsupported in this stream }
  82.   stBase2Error = 8;               { Error in second base
  83.                                     info = base2 error value }
  84.   stMisMatch = 9;                 { Two bases don't match
  85.                                     info = mismatch position in current buffer }
  86.   stIntegrity = 10;               { Stream has detected an integrity error
  87.                                     in a self check.  Info depends on
  88.                                     stream type. }
  89. type
  90.   TOpenMode = $3C00..$3DFF;       { Allowable DOS stream open modes }
  91.   {$ifdef windows}
  92.   FNameStr = PChar;            { To make streams take names as in the manual }
  93.   {$endif}
  94.  
  95.   PFilter = ^TFilter;
  96.   TFilter =
  97.     object(TStream)
  98.     { Generic object to filter another stream.  TFilter just passes everything
  99.       through, and mirrors the status of the base stream }
  100.  
  101.       Base : PStream;
  102.       { Pointer to the base stream. }
  103.  
  104.       Startofs : LongInt;
  105.       { The offset of the start of the filter in the base stream. }
  106.  
  107.       constructor Init(ABase : PStream);
  108.         { Initialize the filter with the given base. }
  109.  
  110.       destructor Done; virtual;
  111.         { Flush filter, then dispose of base. }
  112.  
  113.       function GetPos : LongInt; virtual;
  114.       function GetSize : LongInt; virtual;
  115.       procedure Read(var Buf; Count : Word); virtual;
  116.       procedure Seek(Pos : LongInt); virtual;
  117.       procedure Truncate; virtual;
  118.       procedure Write(var Buf; Count : Word); virtual;
  119.       procedure Flush; virtual;
  120.  
  121.       function CheckStatus : Boolean; virtual;
  122.     { Return true if status is stOK.
  123.       If status is stOK, but base is not, then reset the base.  This is a poor
  124.       substitute for a virtual Reset method. }
  125.  
  126.       procedure CheckBase;
  127.         { Check base stream for error, and copy status using own Error method. }
  128.     end;
  129.  
  130.   PEncryptFilter = ^TEncryptFilter;
  131.   TEncryptFilter =
  132.     object(TFilter)
  133.   { Filter which encrypts text going in or out; encrypting twice with the same
  134.     key decrypts. Not very sophisticated encryption. }
  135.  
  136.       Key : LongInt;
  137.       { Key is used as a Randseed replacement }
  138.  
  139.       constructor Init(Akey : LongInt; ABase : PStream);
  140.         { Init with a given key }
  141.  
  142.       procedure Read(var Buf; Count : Word); virtual;
  143.       procedure Seek(Pos : LongInt); virtual;
  144.       procedure Write(var Buf; Count : Word); virtual;
  145.     end;
  146.  
  147. const
  148.   MaxStack = 4096;                { must match lzwstream.asm declaration! }
  149.  
  150. type
  151.   PLZWTables = ^TLZWTables;
  152.   TLZWTables =
  153.     record
  154.       Collision : array[0..MaxStack-1] of Byte; { Hash table entries }
  155.       PrefixTable : array[0..MaxStack-1] of Word; { Code for preceding stringf }
  156.       SuffixTable : array[0..MaxStack-1] of Byte; { Code for current character }
  157.       ChildTable : array[0..MaxStack-1] of Word; { Next duplicate in collision
  158.                                                  list }
  159.       CharStack : array[0..MaxStack-1] of Byte; { Decompression stack }
  160.       StackPtr : Word;            { Decompression stack depth }
  161.       Prefix : Word;              { Previous code string }
  162.       TableUsed : Word;           { # string table entries used }
  163.       InputPos : Word;            { Index in input buffer }
  164.       OutputPos : Word;           { Index in output buffer }
  165.       LastHit : Word;             { Last empty slot in collision
  166.                                                  table }
  167.       CodeBuf : Word;
  168.       SaveIP : Word;
  169.       SaveAX : Word;
  170.       SaveCX : Word;
  171.       SaveDX : Word;
  172.  
  173.       NotFound : Byte;            { Character combination found
  174.                                                  flag }
  175.     end;
  176.  
  177.   PLZWFilter = ^TLZWFilter;
  178.   TLZWFilter =
  179.     object(TFilter)
  180.       Mode : Word;                { Either stOpenRead or stOpenWrite. }
  181.       Size,                       { The size of the expanded stream. }
  182.       Position : LongInt;         { The current position in the expanded stream }
  183.       Tables : PLZWTables;        { Tables holding the compressor state. }
  184.  
  185.       constructor Init(ABase : PStream; AMode : TOpenMode);
  186.     {  Create new compressor stream, to use ABase as the source/destination
  187.        for data.  Mode must be stOpenRead or stOpenWrite. }
  188.  
  189.       destructor Done; virtual;
  190.     {  Flushes all data to the stream, and writes the uncompressed
  191.        filesize to the head of it before calling TFilter.done. }
  192.  
  193.       procedure Flush; virtual;
  194.       function GetPos : LongInt; virtual;
  195.       function GetSize : LongInt; virtual;
  196.       procedure Read(var Buf; Count : Word); virtual;
  197.  
  198.       procedure Seek(Pos : LongInt); virtual;
  199.     {  Seek is not supported at all in Write mode.  In Read mode, it is
  200.        slow for seeking forwards, and very slow for seeking backwards:
  201.        it rewinds the file to the start and seeks forward from there. }
  202.  
  203.       procedure Truncate; virtual;
  204.     {  Truncate is not supported in either mode, and always causes a
  205.        call to Error. }
  206.  
  207.       procedure Write(var Buf; Count : Word); virtual;
  208.     end;
  209.  
  210. type
  211.   PTextFilter = ^TTextFilter;
  212.   TTextFilter =
  213.     object(TFilter)
  214.   { A filter to provide ReadLn/WriteLn interface to a stream.  First
  215.     open the stream and position it, then pass it to this filter;
  216.     then Reset, Rewrite, or Append the Textfile variable, and do all
  217.     reads and writes to it; they'll go to the stream through a TFDD. }
  218.  
  219.       Textfile : Text;
  220.       { The fake text file to use with Read(ln)/Write(ln) }
  221.  
  222.       constructor Init(ABase : PStream; AName : String);
  223.     { Initialize the interface to ABase; stores AName in the name field of
  224.       Textfile. }
  225.  
  226.       destructor Done; virtual;
  227.         { Flushes the Textfile, then closes and disposes of the base stream. }
  228.  
  229.       function GetPos : LongInt; virtual;
  230.       function GetSize : LongInt; virtual;
  231.       procedure Read(var Buf; Count : Word); virtual;
  232.       procedure Seek(Pos : LongInt); virtual;
  233.       procedure Truncate; virtual;
  234.       procedure Write(var Buf; Count : Word); virtual;
  235.     end;
  236.  
  237.   PLogFilter = ^TLogFilter;
  238.   TLogFilter =
  239.     object(TFilter)
  240.       { A filter to log activity on a text file. }
  241.  
  242.       LogList : ^Text;            { A pointer to the first logged file }
  243.  
  244.       constructor init(ABase:PStream);
  245.       { Initializes filter, but doesn't start logging anything }
  246.  
  247.       destructor Done; virtual;
  248.       { Stops logging all files, and closes & disposes of the base stream }
  249.  
  250.       procedure Log(var F : Text);
  251.     { Logs all input and output to F to the stream.  You must do the Assign to
  252.       F first, and not do another Assign without closing F. }
  253.  
  254.       function Unlog(var F : Text) : Boolean;
  255.     { Stops logging of F.  Called automatically if file is closed. Returns
  256.       false and does nothing on error. }
  257.     end;
  258.  
  259.   TBit = 0..1;                    { A single bit }
  260.  
  261.   PBitFilter = ^TBitFilter;
  262.   TBitFilter =
  263.     object(TFilter)
  264.       BitPos : ShortInt;
  265.       { Position of stream relative to base file.  Negative values signal
  266.         that the buffer is unchanged from the file, positive values signal
  267.         that the file needs to be updated.  Zero signals an empty buffer. }
  268.       Mask : Byte;                { Mask to extract next bit from buffer }
  269.       Buffer : Byte;              { Buffer of next 8 bits from stream }
  270.       AtEnd : Boolean;            { Flag to signal that we're at the end
  271.                                     of the base, and we shouldn't read
  272.                                     it.  Bases that change in length should
  273.                                     set this to false. }
  274.  
  275.       constructor Init(ABase : PStream);
  276.  
  277.       procedure Flush; virtual;   { Flush buffer to stream }
  278.       procedure Seek(Pos : LongInt); virtual; { Seek to bit at start of
  279.                                                pos byte }
  280.       procedure Read(var Buf; Count : Word); virtual;
  281.       procedure Write(var Buf; Count : Word); virtual;
  282.  
  283.       function GetBit : TBit;     { Get next bit from stream }
  284.       function GetBits(Count : Byte) : LongInt; { Get up to 32 bits }
  285.       procedure ReadBits(var Buf; Count : LongInt); { Read bits from stream }
  286.  
  287.       procedure PutBit(ABit : TBit); { Put one bit to stream }
  288.       procedure PutBits(ABits : LongInt; Count : Byte); { Put up to 32 bits }
  289.       procedure WriteBits(var Buf; Count : LongInt); { Write count bits to stream }
  290.  
  291.       procedure SeekBit(Pos : LongInt); { Seek to particular bit }
  292.       function GetBitPos : LongInt;
  293.  
  294.       procedure CopyBits(var S : TBitFilter; Count : LongInt); { Copy bits from S }
  295.       procedure ByteAlign;        { Seek forward to next byte boundary. }
  296.  
  297.       procedure PrepareBuffer(ForRead : Boolean);
  298.         { Internal method to assure that buffer is valid }
  299.     end;
  300.  
  301.   PDupFilter = ^TDupFilter;
  302.   TDupFilter =
  303.     object(TFilter)         { Duplicates output, confirms matching input }
  304.       Base2 : PStream;
  305.       { Pointer to the second base. }
  306.  
  307.       Startofs2 : LongInt;
  308.       { The offset of the start of the filter in the second base. }
  309.  
  310.       constructor Init(ABase, ABase2 : PStream);
  311.         { Initialize the filter with the given bases. }
  312.  
  313.       destructor Done; virtual;
  314.         { Flush filter, then dispose of both bases. }
  315.  
  316.       function MisMatch(var buf1,buf2; count:word):word; virtual;
  317.         { Checks for a mismatch between the two buffers.  Returns
  318.           the byte number of the mismatch (1 based), or 0 if they
  319.           test equal.  This default method checks for an exact match. }
  320.  
  321.       procedure Read(var Buf; Count : Word); virtual;
  322.       procedure Seek(Pos : LongInt); virtual;
  323.       procedure Truncate; virtual;
  324.       procedure Write(var Buf; Count : Word); virtual;
  325.       procedure Flush; virtual;
  326.  
  327.       function CheckStatus : Boolean; virtual;
  328.     { Return true if status is stOK.
  329.       If status is stOK, but base is not, then reset the base.  This is a poor
  330.       substitute for a virtual Reset method. }
  331.  
  332.       procedure CheckBase2;
  333.         { Check 2nd base stream for error, and copy status using own Error method. }
  334.     end;
  335.  
  336.   PSequential = ^TSequential;
  337.   TSequential =
  338.     object(TFilter)                        { Filter for sequential access only }
  339.       procedure Seek(pos:longint); virtual;{ Signals stUnsupported if a Seek is attempted }
  340.     end;
  341.  
  342.   PChksumFilter = ^TChksumFilter;
  343.   TChksumFilter =
  344.     object(TSequential)                    { Calculates 16 bit checksum of
  345.                                              bytes read/written. }
  346.       Chksum : word;
  347.  
  348.       constructor Init(ABase : PStream;AChksum:word);
  349.         { Initialize the filter with the given base and starting checksum. }
  350.  
  351.       procedure Read(var Buf; Count : Word); virtual;
  352.       procedure Write(var Buf; Count : Word); virtual;
  353.     end;
  354.  
  355.   PCRC16Filter = ^TCRC16Filter;
  356.   TCRC16Filter =
  357.     object(TSequential)      { Calculates XMODEM style 16 bit CRC }
  358.       CRC16 : word;
  359.  
  360.       constructor Init(ABase : PStream;ACRC16:word);
  361.         { Initialize the filter with the given base and starting CRC. }
  362.  
  363.       procedure Read(var Buf; Count : Word); virtual;
  364.       procedure Write(var Buf; Count : Word); virtual;
  365.     end;
  366.  
  367.   PCRCARCFilter = ^TCRCARCFilter;
  368.   TCRCARCFilter =
  369.     object(TSequential)      { Calculates ARC-style 16 bit CRC }
  370.       CRCARC : word;
  371.  
  372.       constructor Init(ABase : PStream;ACRCARC:word);
  373.         { Initialize the filter with the given base and starting CRC. }
  374.  
  375.       procedure Read(var Buf; Count : Word); virtual;
  376.       procedure Write(var Buf; Count : Word); virtual;
  377.     end;
  378.  
  379.   PCRC32Filter = ^TCRC32Filter;
  380.   TCRC32Filter =
  381.     object(TSequential)      { Calculates PKZIP and ZModem style 32 bit CRC }
  382.       CRC32 : longint;
  383.  
  384.       constructor Init(ABase : PStream;ACRC32:longint);
  385.         { Initialize the filter with the given base and starting CRC. }
  386.  
  387.       procedure Read(var Buf; Count : Word); virtual;
  388.       procedure Write(var Buf; Count : Word); virtual;
  389.     end;
  390.  
  391.  
  392.   PNulStream = ^TNulStream;
  393.   TNulStream =
  394.     object(TStream)
  395.       Position : LongInt;         { The current position for the stream. }
  396.       Value : Byte;               { The value returned on reads. }
  397.  
  398.       constructor Init(AValue : Byte);
  399.       function GetPos : LongInt; virtual;
  400.       function GetSize : LongInt; virtual;
  401.       procedure Read(var Buf; Count : Word); virtual;
  402.       procedure Seek(Pos : LongInt); virtual;
  403.       procedure Write(var Buf; Count : Word); virtual;
  404.     end;
  405.  
  406.   Pbyte_array = ^Tbyte_array;
  407.   Tbyte_array = array[0..65520] of Byte; { Type used as a buffer. }
  408.  
  409.   PRAMStream = ^TRAMStream;
  410.   TRAMStream =
  411.     object(TStream)
  412.       Position : Word;            { The current position for the stream. }
  413.  
  414.       Size : Word;                { The current size of the stream. }
  415.       Alloc : Word;               { The size of the allocated block of memory. }
  416.  
  417.       Buffer : Pbyte_array;       { Points to the stream data. }
  418.       OwnMem : Boolean;           { Whether Done should dispose of data.}
  419.  
  420.       constructor Init(Asize : Word);
  421.     { Attempt to initialize the stream to a block size of Asize;
  422.        initial stream size and position are 0. }
  423.       constructor UseBuf(ABuffer : Pointer; Asize : Word);
  424.      { Initialize the stream using the specified buffer.  OwnMem is set
  425.        to false, so the buffer won't be disposed of. }
  426.  
  427.       destructor Done; virtual;
  428.         { Dispose of the stream. }
  429.  
  430.       function GetPos : LongInt; virtual;
  431.       function GetSize : LongInt; virtual;
  432.       procedure Read(var Buf; Count : Word); virtual;
  433.       procedure Seek(Pos : LongInt); virtual;
  434.       procedure Truncate; virtual;
  435.       procedure Write(var Buf; Count : Word); virtual;
  436.     end;
  437.  
  438.   PXMSStream = ^TXMSStream;
  439.   TXMSStream =
  440.     object(TStream)
  441.       Handle : Word;              { XMS handle }
  442.       MaxBlocks : Word;           { Max 1K blocks to allocate }
  443.       BlocksUsed : Word;          { Number of 1K blocks used. Always allocates
  444.                                     at least one byte more than Size. }
  445.       Size : LongInt;             { The current size of the stream }
  446.       Position : LongInt;         { Current position }
  447.  
  448.       constructor Init(AMaxBlocks : Word);
  449.       destructor Done; virtual;
  450.  
  451.       function GetPos : LongInt; virtual;
  452.       function GetSize : LongInt; virtual;
  453.       procedure Read(var Buf; Count : Word); virtual;
  454.       procedure Seek(Pos : LongInt); virtual;
  455.       procedure Truncate; virtual;
  456.       procedure Write(var Buf; Count : Word); virtual;
  457.  
  458.       procedure NewBlock;         { Internal method to allocate a block }
  459.       procedure FreeBlock;        { Internal method to free one block }
  460.     end;
  461.  
  462. function xms_MemAvail : Word;
  463.   { Returns number of available XMS blocks. }
  464. function xms_MaxAvail : Word;
  465.   { Returns size of largest available XMS block. }
  466.  
  467. type
  468.   PNamedBufStream = ^TNamedBufStream;
  469.   TNamedBufStream =
  470.     object(TBufStream)
  471.       { A simple descendant of TBufStream which knows its own name. }
  472.  
  473.     {$ifdef windows}
  474.     filename : PChar;
  475.     {$else}
  476.       Filename : PString;
  477.     {$endif}
  478.       { The name of the stream. }
  479.  
  480.       constructor Init(Name : FNameStr; Mode : TOpenMode; ABufSize : Word);
  481.         { Open the file with the given name, and save the name. }
  482.  
  483.       destructor Done; virtual;
  484.         { Close the file. }
  485.  
  486.     end;
  487.  
  488.   PTempBufStream = ^TTempBufStream;
  489.   TTempBufStream =
  490.     object(TNamedBufStream)
  491.       { A temporary buffered file stream, which deletes itself when done.}
  492.  
  493.       constructor Init(ABufSize : Word);
  494.   { Create a temporary file with a unique name, in the directory
  495.     pointed to by the environment varable TEMP or in the current
  496.     directory, and open it in read/write mode.   }
  497.  
  498.       destructor Done; virtual;
  499.         { Close and delete the temporary file. }
  500.  
  501.     end;
  502.  
  503. type
  504.   TStreamType = (NoStream, RAMStream, EMSStream, XMSStream, FileStream);
  505.   { The type of stream that a tempstream might be. }
  506.  
  507. const
  508.   NumTypes = Ord(FileStream);
  509.   BufSize : Word = 2048;          { Buffer size if buffered stream is used. }
  510.  
  511. type
  512.   TStreamRanking = array[1..NumTypes] of TStreamType;
  513.   { A ranking of preference for a type of stream, from most to least preferred }
  514.  
  515. const ForSpeed : TStreamRanking = (RAMStream, EMSStream, XMSStream, FileStream);
  516.   { Streams ordered for speed }
  517.  
  518. const ForSize : TStreamRanking = (FileStream, EMSStream, XMSStream, RAMStream);
  519.   { Streams ordered for low impact on the heap }
  520.  
  521. const ForSizeInMem : TStreamRanking = (EMSStream, XMSStream, RAMStream, NoStream);
  522.   { Streams in memory only, ordered as #ForSize#. }
  523.  
  524. const ForOverlays : TStreamRanking = (EMSStream, XMSStream, FileStream, NoStream);
  525.   { Streams ordered for speed, but never in RAM. }
  526.  
  527. function TempStream(InitSize, MaxSize : LongInt;
  528.                     Preference : TStreamRanking) : PStream;
  529.  
  530. {      This procedure returns a pointer to a temporary stream from a
  531.        choice of 3, specified in the Preference array.  The first stream
  532.        type listed in the Preference array which can be successfully
  533.        created with the given sizes will be returned, or Nil if none can
  534.        be made. }
  535.  
  536. {$ifndef windows}
  537. procedure OvrInitStream(S : PStream);
  538. { Copies overlay segment code to S as new segments are loaded,
  539.   and does reloads from there.  Allows multiple calls, to buffer
  540.   different segments on different streams. }
  541.  
  542. procedure OvrDetachStream(BadS : PStream);
  543.   { Makes sure that the overlay system makes no references to BadS. }
  544.  
  545. procedure OvrDisposeStreams;
  546.   { Detaches and disposes of all streams being used by the overlay system }
  547.  
  548. function OvrSizeNeeded : LongInt;
  549. { Returns the size required to load any segments which still haven't
  550.   been loaded to a stream. }
  551.  
  552. function OvrLoadAll : Boolean;
  553. { Forces all overlay segments to be copied into the stream; if successful
  554.   (true) then no more references to the overlay file will be made. }
  555. {$endif windows}
  556.  
  557. Function UpdateChksum(Initsum: Word; Var InBuf; InLen : Word) : Word;
  558. { Updates the checksum Initsum by adding InLen bytes from InBuf }
  559.  
  560. Function UpdateCRC16(InitCRC : Word; Var InBuf; InLen : Word) : Word;
  561. { I believe this is the CRC used by the XModem protocol.  The transmitting
  562.   end should initialize with zero, UpdateCRC16 for the block, Continue the
  563.   UpdateCRC16 for two nulls, and append the result (hi order byte first) to
  564.   the transmitted block.  The receiver should initialize with zero and
  565.   UpdateCRC16 for the received block including the two byte CRC.  The
  566.   result will be zero (why?) if there were no transmission errors.  (I have
  567.   not tested this function with an actual XModem implementation, though I
  568.   did verify the behavior just described.  See TESTCRC.PAS.) }
  569.  
  570.  
  571. Function UpdateCRCArc(InitCRC : Word; Var InBuf; InLen : Word) : Word;
  572. { This function computes the CRC used by SEA's ARC utility.  Initialize
  573.   with zero. }
  574.  
  575. Function UpdateCRC32(InitCRC : LongInt; Var InBuf; InLen : Word) : LongInt;
  576. { This function computes the CRC used by PKZIP and Forsberg's ZModem.
  577.   Initialize with high-values ($FFFFFFFF), and finish by inverting all bits
  578.   (Not). }
  579.  
  580. implementation
  581.  
  582.   constructor TFilter.Init(ABase : PStream);
  583.   begin
  584.     TStream.Init;
  585.     Base := ABase;
  586.     CheckBase;
  587.     if Status = stOK then
  588.       Startofs := Base^.GetPos;
  589.   end;
  590.  
  591.   destructor TFilter.Done;
  592.   begin
  593.     if Base <> nil then
  594.     begin
  595.       Flush;
  596.       Dispose(Base, Done);
  597.     end;
  598.     TStream.Done;
  599.   end;
  600.  
  601.   function TFilter.GetPos : LongInt;
  602.   begin
  603.     if CheckStatus then
  604.     begin
  605.       GetPos := Base^.GetPos-Startofs;
  606.       CheckBase;
  607.     end;
  608.   end;
  609.  
  610.   function TFilter.GetSize : LongInt;
  611.   begin
  612.     if CheckStatus then
  613.     begin
  614.       GetSize := Base^.GetSize-Startofs;
  615.       CheckBase;
  616.     end;
  617.   end;
  618.  
  619.   procedure TFilter.Read(var Buf; Count : Word);
  620.   begin
  621.     if CheckStatus then
  622.     begin
  623.       Base^.Read(Buf, Count);
  624.       CheckBase;
  625.     end;
  626.   end;
  627.  
  628.   procedure TFilter.Seek(Pos : LongInt);
  629.   begin
  630.     if CheckStatus then
  631.     begin
  632.       Base^.Seek(Pos+Startofs);
  633.       CheckBase;
  634.     end;
  635.   end;
  636.  
  637.   procedure TFilter.Truncate;
  638.   begin
  639.     if CheckStatus then
  640.     begin
  641.       Base^.Truncate;
  642.       CheckBase;
  643.     end;
  644.   end;
  645.  
  646.   procedure TFilter.Write(var Buf; Count : Word);
  647.   begin
  648.     if CheckStatus then
  649.     begin
  650.       Base^.Write(Buf, Count);
  651.       CheckBase;
  652.     end;
  653.   end;
  654.  
  655.   procedure TFilter.Flush;
  656.   begin
  657.     if CheckStatus then
  658.     begin
  659.       Base^.Flush;
  660.       CheckBase;
  661.     end;
  662.   end;
  663.  
  664.   function TFilter.CheckStatus : Boolean;
  665.   begin
  666.     if (Status = stOK) and (Base^.Status <> stOK) then
  667.       Base^.Reset;
  668.     CheckStatus := Status = stOK;
  669.   end;
  670.  
  671.   procedure TFilter.CheckBase;
  672.   begin
  673.     if Base^.Status <> stOK then
  674.       Error(stBaseError, Base^.Status);
  675.   end;
  676.  
  677.   constructor TEncryptFilter.Init(Akey : LongInt; ABase : PStream);
  678.   begin
  679.     TFilter.Init(ABase);
  680.     Key := Akey;
  681.   end;
  682.  
  683.   procedure TEncryptFilter.Read(var Buf; Count : Word);
  684.   var
  685.     i : Word;
  686.     SaveSeed : LongInt;
  687.     Bytes : Tbyte_array absolute Buf;
  688.   begin
  689.     SaveSeed := RandSeed;
  690.     RandSeed := Key;
  691.     TFilter.Read(Buf, Count);
  692.     for i := 0 to Count-1 do
  693.       Bytes[i] := Bytes[i] xor Random(256);
  694.     Key := RandSeed;
  695.     RandSeed := SaveSeed;
  696.   end;
  697.  
  698.   procedure CycleKey(Key, Cycles : LongInt);
  699. { For cycles > 0, mimics cycles calls to the TP random number generator.
  700.   For cycles < 0, backs it up the given number of calls. }
  701.   var
  702.     i : LongInt;
  703.     Junk : Integer;
  704.     SaveSeed : LongInt;
  705.   begin
  706.     if Cycles > 0 then
  707.     begin
  708.       SaveSeed := RandSeed;
  709.       RandSeed := Key;
  710.       for i := 1 to Cycles do
  711.         Junk := Random(0);
  712.       Key := RandSeed;
  713.       RandSeed := Key;
  714.     end
  715.     else
  716.       for i := -1 downto Cycles do
  717.         Key := (Key-1)*(-649090867);
  718.   end;
  719.  
  720.   procedure TEncryptFilter.Seek(Pos : LongInt);
  721.   var
  722.     OldPos : LongInt;
  723.   begin
  724.     OldPos := GetPos;
  725.     TFilter.Seek(Pos);
  726.     CycleKey(Key, Pos-OldPos);
  727.   end;
  728.  
  729.   procedure TEncryptFilter.Write(var Buf; Count : Word);
  730.   var
  731.     i : Word;
  732.     SaveSeed : LongInt;
  733.     BufPtr : ^Byte;
  734.     BufPtrOffset : Word absolute BufPtr;
  735.     Buffer : array[0..255] of Byte;
  736.   begin
  737.     SaveSeed := RandSeed;
  738.     RandSeed := Key;
  739.     BufPtr := @Buf;
  740.     while Count > 256 do
  741.     begin
  742.       Move(BufPtr^, Buffer, 256);
  743.       for i := 0 to 255 do
  744.         Buffer[i] := Buffer[i] xor Random(256);
  745.       TFilter.Write(Buffer, 256);
  746.       Dec(Count, 256);
  747.       Inc(BufPtrOffset, 256);
  748.     end;
  749.     Move(BufPtr^, Buffer, Count);
  750.     for i := 0 to Count-1 do
  751.       Buffer[i] := Buffer[i] xor Random(256);
  752.     TFilter.Write(Buffer, Count);
  753.     Key := RandSeed;
  754.     RandSeed := SaveSeed;
  755.   end;
  756.  
  757.  
  758.   { ******* LZW code ******* }
  759.  
  760. {$L LZWSTREAM.OBJ}
  761.  
  762.   procedure Initialise(Tables : PLZWTables); External;
  763.  
  764.   function PutSignature(Tables : PLZWTables) : Boolean; External;
  765.  
  766.   function Crunch(InBufSize, OutBufSize : Word;
  767.                   var InBuffer, OutBuffer;
  768.   Tables : PLZWTables) : Pointer; External;
  769.  
  770. {  Crunch some more text.  Stops when Inbufsize bytes are used up, or
  771.    output buffer is full.   Returns bytes used in segment, bytes written
  772.    in offset of result }
  773.  
  774.   function FlushLZW(var OutBuffer;
  775.   Tables : PLZWTables) : Word; External;
  776. {  Flush the remaining characters to signal EOF.  Needs space for up to
  777.    3 characters. }
  778.  
  779.   function GetSignature(var InBuffer, Dummy;
  780.   Tables : PLZWTables) : Boolean; External;
  781. { Initializes for reading, and checks for 'LZ' signature in start of compressed
  782.   code.  Inbuffer must contain at least 3 bytes.  Dummy is just there to put the
  783.   Inbuffer in the right spot }
  784.  
  785.   function Uncrunch(InBufSize, OutBufSize : Word;
  786.                     var InBuffer, OutBuffer;
  787.   Tables : PLZWTables) : Pointer; External;
  788. {  Uncrunch some text.  Will stop when it has done Outbufsize worth or has
  789.    exhausted Inbufsize worth.  Returns bytes used in segment, bytes written
  790.    in offset of result }
  791.  
  792.   constructor TLZWFilter.Init(ABase : PStream; AMode : TOpenMode);
  793.     {  Create new compressor stream, to use ABase as the source/destination
  794.        for data.  Mode must be stOpenRead or stOpenWrite. }
  795.   var
  796.     Buffer : array[1..3] of Byte;
  797.     Info : Integer;
  798.   begin
  799.     Info := stBadMode;
  800.     if (AMode = stOpenRead) or (AMode = stOpenWrite) then
  801.     begin
  802.       Info := stStreamFail;
  803.       if TFilter.Init(ABase) then
  804.       begin
  805.         if Status = stOK then
  806.         begin
  807.           Info := stMemError;
  808.           Startofs := Base^.GetPos;
  809.           Position := 0;
  810.           Mode := AMode;
  811.  
  812.           if MaxAvail >= SizeOf(TLZWTables) then
  813.           begin
  814.             Info := stSigError;
  815.             GetMem(Tables, SizeOf(TLZWTables));
  816.             Initialise(Tables);
  817.             if Mode = stOpenRead then
  818.             begin
  819.               Base^.Read(Size, SizeOf(Size));
  820.               Base^.Read(Buffer, 3);
  821.               CheckBase;
  822.               if GetSignature(Buffer, Buffer, Tables) then
  823.                 Exit;             { Successfully opened for reading }
  824.             end
  825.             else if Mode = stOpenWrite then
  826.             begin
  827.               Size := 0;
  828.               Base^.Write(Size, SizeOf(Size)); { Put a place holder }
  829.               CheckBase;
  830.               if PutSignature(Tables) then
  831.                 Exit;             { Successful construction for writing! }
  832.             end;
  833.           end;
  834.         end;
  835.       end;
  836.     end;
  837.     Error(stInitError, Info);
  838.   end;
  839.  
  840.   destructor TLZWFilter.Done;
  841.   begin
  842.     Flush;
  843.     FreeMem(Tables, SizeOf(TLZWTables));
  844.     TFilter.Done;
  845.   end;
  846.  
  847.   procedure TLZWFilter.Write(var Buf; Count : Word);
  848.   var
  849.     Inbuf : array[0..65520] of Byte absolute Buf;
  850.     Outbuf : array[0..255] of Byte;
  851.     Inptr : Word;
  852.     Sizes : record
  853.               OutSize, UsedSize : Word;
  854.             end;
  855.   begin
  856.     if CheckStatus then
  857.     begin
  858.       if Mode <> stOpenWrite then
  859.         Error(stBadMode, Mode);
  860.       Inptr := 0;
  861.       repeat
  862.         Pointer(Sizes) := Crunch(Count, SizeOf(Outbuf),
  863.                                  Inbuf[Inptr], Outbuf, Tables);
  864.         with Sizes do
  865.         begin
  866.           Base^.Write(Outbuf, OutSize);
  867.  
  868.           Dec(Count, UsedSize);
  869.           Inc(Inptr, UsedSize);
  870.           Inc(Size, UsedSize);
  871.           Inc(Position, UsedSize);
  872.         end;
  873.       until Count = 0;
  874.       CheckBase;
  875.     end;
  876.   end;
  877.  
  878.   procedure TLZWFilter.Flush;
  879.   var
  880.     Outbuf : array[0..255] of Byte;
  881.     Sizes : record
  882.               OutSize, UsedSize : Word;
  883.             end;
  884.     Pos : LongInt;
  885.   begin
  886.     if CheckStatus then
  887.     begin
  888.       if Mode = stOpenWrite then
  889.       begin
  890.         Pointer(Sizes) := Crunch(1, SizeOf(Outbuf), Outbuf, Outbuf, Tables);
  891.         { Push one more character to match JA bug }
  892.         with Sizes do
  893.         begin
  894.           Base^.Write(Outbuf, OutSize);
  895.  
  896.           OutSize := FlushLZW(Outbuf, Tables); { And flush }
  897.           Base^.Write(Outbuf, OutSize);
  898.         end;
  899.         Pos := Base^.GetPos;
  900.         Base^.Seek(Startofs);
  901.         Base^.Write(Size, SizeOf(Size));
  902.         Base^.Seek(Pos);
  903.       end;
  904.       Base^.Flush;
  905.       Mode := 0;
  906.       CheckBase;
  907.     end;
  908.   end;
  909.  
  910.   procedure TLZWFilter.Read(var Buf; Count : Word);
  911.   var
  912.     Outbuf : array[0..65520] of Byte absolute Buf;
  913.     Inbuf : array[0..255] of Byte;
  914.     OutPtr : Word;
  915.     BlockSize : Word;
  916.     Sizes : record
  917.               OutSize, UsedSize : Word;
  918.             end;
  919.     BytesLeft : LongInt;
  920.   begin
  921.     if CheckStatus then
  922.     begin
  923.       if Mode <> stOpenRead then
  924.         Error(stBadMode, Mode);
  925.       OutPtr := 0;
  926.       BlockSize := SizeOf(Inbuf);
  927.       with Base^ do
  928.         BytesLeft := GetSize-GetPos;
  929.  
  930.       if Position+Count > Size then
  931.       begin
  932.         Error(stReaderror, 0);
  933.         FillChar(Buf, Count, 0);
  934.         Exit;
  935.       end;
  936.  
  937.       while Count > 0 do
  938.       begin
  939.         if BytesLeft < BlockSize then
  940.           BlockSize := BytesLeft;
  941.         Base^.Read(Inbuf, BlockSize);
  942.         Pointer(Sizes) := Uncrunch(BlockSize, Count, Inbuf,
  943.                                    Outbuf[OutPtr], Tables);
  944.         with Sizes do
  945.         begin
  946.           if OutSize = 0 then
  947.           begin
  948.             Error(stReaderror, 0);
  949.             FillChar(Outbuf[OutPtr], Count, 0);
  950.             Exit;
  951.           end;
  952.           Dec(BytesLeft, UsedSize);
  953.           Inc(Position, OutSize);
  954.           Dec(Count, OutSize);
  955.           Inc(OutPtr, OutSize);
  956.           if UsedSize < BlockSize then
  957.             with Base^ do         { seek back to the first unused byte }
  958.               Seek(GetPos-(BlockSize-UsedSize));
  959.         end;
  960.       end;
  961.       CheckBase;
  962.     end;
  963.   end;
  964.  
  965.   procedure TLZWFilter.Seek(Pos : LongInt);
  966.   var
  967.     Buf : array[0..255] of Byte;
  968.     Bytes : Word;
  969.   begin
  970.     if CheckStatus then
  971.     begin
  972.       if Mode <> stOpenRead then
  973.       begin
  974.         Error(stBadMode, Mode);
  975.         Exit;
  976.       end;
  977.       if Pos < Position then
  978.       begin
  979.         Base^.Seek(Startofs);
  980.         FreeMem(Tables, SizeOf(TLZWTables));
  981.  
  982.         TLZWFilter.Init(Base, Mode); { Re-initialize everything.  Will this cause
  983.                                      bugs in descendents? }
  984.       end;
  985.       while Pos > Position do
  986.       begin
  987.         if Pos-Position > SizeOf(Buf) then
  988.           Bytes := SizeOf(Buf)
  989.         else
  990.           Bytes := Pos-Position;
  991.         Read(Buf, Bytes);
  992.       end;
  993.     end;
  994.   end;
  995.  
  996.   procedure TLZWFilter.Truncate;
  997.   begin
  998.     Error(stBadMode, Mode);
  999.   end;
  1000.  
  1001.   function TLZWFilter.GetPos;
  1002.   begin
  1003.     GetPos := Position;
  1004.   end;
  1005.  
  1006.   function TLZWFilter.GetSize;
  1007.   begin
  1008.     GetSize := Size;
  1009.   end;
  1010.  
  1011.   { ***** Text Filter Code ******* }
  1012.  
  1013.   { These declarations are used both by TTextFilter and TLogFilter }
  1014.  
  1015. type
  1016.   TFDDfunc = function(var F : Text) : Integer;
  1017.  
  1018.   PStreamTextRec = ^StreamTextRec;
  1019.   PSaveText = ^TSaveText;
  1020.   TSaveText =
  1021.     record                        { Used when logging for original data values }
  1022.       OpenFunc,
  1023.       InOutFunc,
  1024.       FlushFunc,
  1025.       CloseFunc : TFDDfunc;
  1026.       S : PLogFilter;
  1027.       SaveData : PSaveText;
  1028.       Next : PStreamTextRec;
  1029.       Data : array[13..16] of Byte;
  1030.     end;
  1031.  
  1032.   StreamTextRec =
  1033.     record
  1034.       Handle : Word;
  1035.       Mode : Word;
  1036.       BufSize : Word;
  1037.       private : Word;
  1038.       BufPos : Word;
  1039.       BufEnd : Word;
  1040.       BufPtr : Pbyte_array;
  1041.       OpenFunc,
  1042.       InOutFunc,
  1043.       FlushFunc,
  1044.       CloseFunc : TFDDfunc;
  1045.       S : PFilter;                { This is a TTextFilter or a TLogFilter }
  1046.       SaveData : PSaveText;
  1047.       Next : PStreamTextRec;
  1048.       OtherData : array[13..16] of Byte;
  1049.       Name : array[0..79] of Char;
  1050.       Buffer : array[0..127] of Byte;
  1051.     end;
  1052.  
  1053.   function TextIn(var F : Text) : Integer; Far;
  1054.   var
  1055.     savemode : word;
  1056.   begin
  1057.     with StreamTextRec(F), S^ do
  1058.     begin
  1059.       if Status = 0 then
  1060.       begin
  1061.         savemode := mode;
  1062.         mode := fmClosed;               { This stops infinite loop }
  1063.         if GetSize-GetPos > BufSize then
  1064.         begin
  1065.           Read(BufPtr^, BufSize);
  1066.           BufEnd := BufSize;
  1067.         end
  1068.         else
  1069.         begin
  1070.           BufEnd := GetSize-GetPos;
  1071.           if BufEnd > 0 then
  1072.             Read(BufPtr^, BufEnd);
  1073.         end;
  1074.         mode := savemode;
  1075.       end;
  1076.       TextIn := Status;
  1077.     end;
  1078.   end;
  1079.  
  1080.   function TextOut(var F : Text) : Integer; Far;
  1081.   var
  1082.     savemode : word;
  1083.   begin
  1084.     with StreamTextRec(F), S^ do
  1085.     begin
  1086.       if Status = 0 then
  1087.       begin
  1088.         savemode := mode;
  1089.         mode := fmClosed;
  1090.         Write(BufPtr^, BufPos);
  1091.         mode := savemode;
  1092.         BufPos := 0;
  1093.       end;
  1094.       TextOut := Status;
  1095.     end;
  1096.   end;
  1097.  
  1098.   function TextInFlush(var F : Text) : Integer; Far;
  1099.   begin
  1100.   end;
  1101.  
  1102.   function TextOutFlush(var F : Text) : Integer; Far;
  1103.   begin
  1104.     TextOutFlush := TextOut(F);
  1105.   end;
  1106.  
  1107.   function TextClose(var F : Text) : Integer; Far;
  1108.   begin
  1109.     TextClose := StreamTextRec(F).S^.Status;
  1110.   end;
  1111.  
  1112.   function TextOpen(var F : Text) : Integer; Far;
  1113.   begin
  1114.     with StreamTextRec(F) do
  1115.     begin
  1116.       case Mode of
  1117.         fmInOut : Mode := fmOutput;
  1118.         fmOutput :
  1119.         begin
  1120.                   Mode := fmClosed;
  1121.                   S^.Seek(S^.Startofs);
  1122.                   Mode := fmOutput;
  1123.         end;
  1124.       end;
  1125.       case Mode of
  1126.         fmInput : begin
  1127.                     InOutFunc := TextIn;
  1128.                     FlushFunc := TextInFlush;
  1129.                   end;
  1130.         fmOutput : begin
  1131.                      InOutFunc := TextOut;
  1132.                      FlushFunc := TextOutFlush;
  1133.                    end;
  1134.       end;
  1135.       TextOpen := S^.Status;
  1136.     end;
  1137.   end;
  1138.  
  1139.   constructor TTextFilter.Init(ABase : PStream; AName : String);
  1140.   begin
  1141.     if not TFilter.Init(ABase) then
  1142.       Fail;
  1143.     with StreamTextRec(Textfile) do
  1144.     begin
  1145.       Mode := fmClosed;
  1146.       BufSize := SizeOf(Buffer);
  1147.       BufPtr := @Buffer;
  1148.       OpenFunc := TextOpen;
  1149.       CloseFunc := TextClose;
  1150.       AName := Copy(AName, 1, 79);
  1151.       Move(AName[1], Name, Length(AName));
  1152.       Name[Length(AName)] := #0;
  1153.       S := @Self;
  1154.     end;
  1155.   end;
  1156.  
  1157.   destructor TTextFilter.Done;
  1158.   begin
  1159.     if StreamTextRec(Textfile).Mode <> fmClosed then
  1160.       Close(Textfile);
  1161.     TFilter.Done;
  1162.   end;
  1163.  
  1164.   function TTextFilter.GetPos : LongInt;
  1165.   begin
  1166.     if StreamTextRec(Textfile).Mode <> fmClosed then
  1167.       System.Flush(TextFile);
  1168.     GetPos := TFilter.GetPos;
  1169.   end;
  1170.  
  1171.   function TTextFilter.GetSize : LongInt;
  1172.   begin
  1173.     if StreamTextRec(Textfile).Mode <> fmClosed then
  1174.       System.Flush(TextFile);
  1175.     GetSize := TFilter.GetSize;
  1176.   end;
  1177.  
  1178.   procedure TTextFilter.Read(var Buf; Count : Word);
  1179.   begin
  1180.     if StreamTextRec(Textfile).Mode <> fmClosed then
  1181.       System.Flush(TextFile);
  1182.     TFilter.Read(Buf,Count);
  1183.   end;
  1184.  
  1185.   procedure TTextFilter.Seek(Pos : LongInt);
  1186.   begin
  1187.     if StreamTextRec(Textfile).Mode <> fmClosed then
  1188.       System.Flush(TextFile);
  1189.     TFilter.Seek(Pos);
  1190.   end;
  1191.  
  1192.   procedure TTextFilter.Truncate;
  1193.   begin
  1194.     if StreamTextRec(Textfile).Mode <> fmClosed then
  1195.       System.Flush(TextFile);
  1196.     TFilter.Truncate;
  1197.   end;
  1198.  
  1199.   procedure TTextFilter.Write(var Buf; Count : Word);
  1200.   begin
  1201.     if StreamTextRec(Textfile).Mode <> fmClosed then
  1202.       System.Flush(TextFile);
  1203.     TFilter.Write(Buf,Count);
  1204.   end;
  1205.  
  1206.   function DoOldCall(Func : TFDDfunc; var F : Text) : Integer;
  1207.   var
  1208.     Save : TSaveText;
  1209.   begin
  1210.     if @Func <> nil then
  1211.       with StreamTextRec(F) do
  1212.       begin
  1213.         Move(OpenFunc, Save, SizeOf(TSaveText));
  1214.         Move(SaveData^, OpenFunc, SizeOf(TSaveText)); { Now using old functions }
  1215.         DoOldCall := Func(F);
  1216.         Move(OpenFunc, Save.SaveData^, SizeOf(TSaveText)); { Save any changes }
  1217.         Move(Save, OpenFunc, SizeOf(TSaveText)); { Back to new ones }
  1218.       end;
  1219.   end;
  1220.  
  1221.   function LogIn(var F : Text) : Integer; Far;
  1222.   var
  1223.     Result : Integer;
  1224.   begin
  1225.     with StreamTextRec(F) do
  1226.     begin
  1227.       Result := DoOldCall(SaveData^.InOutFunc, F);
  1228.       if Result = 0 then
  1229.         S^.Write(BufPtr^, BufEnd); { Might want to record errors
  1230.                                                here }
  1231.       LogIn := Result;
  1232.     end;
  1233.   end;
  1234.  
  1235.   function LogOut(var F : Text) : Integer; Far;
  1236.   begin
  1237.     with StreamTextRec(F) do
  1238.     begin
  1239.       S^.Write(BufPtr^, BufPos);
  1240.       LogOut := DoOldCall(SaveData^.InOutFunc, F);
  1241.     end;
  1242.   end;
  1243.  
  1244.   function LogInFlush(var F : Text) : Integer; Far;
  1245.   begin
  1246.     with StreamTextRec(F) do
  1247.       LogInFlush := DoOldCall(SaveData^.FlushFunc, F);
  1248.   end;
  1249.  
  1250.   function LogOutFlush(var F : Text) : Integer; Far;
  1251.   var
  1252.     OldPos : Word;
  1253.   begin
  1254.     with StreamTextRec(F) do
  1255.     begin
  1256.       OldPos := BufPos;
  1257.       LogOutFlush := DoOldCall(SaveData^.FlushFunc, F);
  1258.       if BufPos = 0 then
  1259.         S^.Write(BufPtr^, OldPos);
  1260.     end;
  1261.   end;
  1262.  
  1263.   function LogClose(var F : Text) : Integer; Far;
  1264.   begin
  1265.     with StreamTextRec(F) do
  1266.     begin
  1267.       LogClose := DoOldCall(SaveData^.CloseFunc, F);
  1268.       if not PLogFilter(S)^.Unlog(F) then
  1269.         { Bug! } ;
  1270.     end;
  1271.   end;
  1272.  
  1273.   function LogOpen(var F : Text) : Integer; Far;
  1274.   begin
  1275.     with StreamTextRec(F) do
  1276.     begin
  1277.       LogOpen := DoOldCall(SaveData^.OpenFunc, F);
  1278.       case Mode of
  1279.         fmInOut, fmOutput : begin
  1280.                               InOutFunc := LogOut;
  1281.                               if @FlushFunc <> nil then
  1282.                                 FlushFunc := LogOutFlush;
  1283.                             end;
  1284.         fmInput : begin
  1285.                     InOutFunc := LogIn;
  1286.                     if @FlushFunc <> nil then
  1287.                       FlushFunc := LogInFlush;
  1288.                   end;
  1289.       end;
  1290.     end;
  1291.   end;
  1292.  
  1293.   { ******* TLogFilter methods ******** }
  1294.  
  1295.   constructor TLogFilter.Init(Abase:PStream);
  1296.   begin
  1297.     if not TFilter.init(ABase) then
  1298.       fail;
  1299.     LogList := nil;
  1300.   end;
  1301.  
  1302.   destructor TLogFilter.Done;
  1303.   begin
  1304.     while (LogList <> nil) and Unlog(LogList^) do ;
  1305.     TFilter.Done;
  1306.   end;
  1307.  
  1308.   procedure TLogFilter.Log(var F : Text);
  1309.   var
  1310.     Save : PSaveText;
  1311.     OldOpen : TFDDfunc;
  1312.     Junk : Integer;
  1313.  
  1314.   begin
  1315.     New(Save);
  1316.     with StreamTextRec(F) do
  1317.     begin
  1318.       Move(OpenFunc, Save^, SizeOf(TSaveText)); { Save the original contents }
  1319.       S := @Self;
  1320.       SaveData := Save;
  1321.       Next := PStreamTextRec(LogList);
  1322.       LogList := @F;              { Insert this file into the list of logged files }
  1323.       OldOpen := SaveData^.OpenFunc;
  1324.       Pointer(@SaveData^.OpenFunc) := nil; { Call LogOpen, but don't open. }
  1325.       Junk := LogOpen(F);
  1326.       SaveData^.OpenFunc := OldOpen;
  1327.       CloseFunc := LogClose;
  1328.     end;
  1329.   end;
  1330.  
  1331.   function TLogFilter.Unlog(var F : Text) : Boolean;
  1332.   var
  1333.     Save : PSaveText;
  1334.     Prev : PStreamTextRec;
  1335.   begin
  1336.     Unlog := False;               { Assume failure }
  1337.     with StreamTextRec(F) do
  1338.     begin
  1339.       if S = @Self then
  1340.       begin
  1341.         { First, delete it from the list. }
  1342.         if LogList = @F then
  1343.           LogList := Pointer(Next)
  1344.         else
  1345.         begin
  1346.           Prev := PStreamTextRec(LogList);
  1347.           while (Prev^.Next <> nil) and (Prev^.Next <> @F) do
  1348.             Prev := Prev^.Next;
  1349.           if Prev^.Next <> @F then
  1350.             Exit;                 { Couldn't find it in the list!? }
  1351.           Prev^.Next := Next;
  1352.         end;
  1353.         Save := SaveData;
  1354.         Move(Save^, OpenFunc, SizeOf(TSaveText));
  1355.         Dispose(Save);
  1356.         Unlog := True;
  1357.       end;
  1358.     end;
  1359.   end;
  1360.  
  1361. {$ifndef windows}
  1362.  
  1363.   { ****** Overlay stream code ****** }
  1364.  
  1365. type
  1366.   { This is the structure at the start of each "thunk" segment }
  1367.   POvrhead = ^TOvrhead;
  1368.   TOvrhead = record
  1369.                Signature : Word;  { CD 3F  - INT 3F call used on returns }
  1370.                Ret_Ofs : Word;    { The offset to jump to when a return triggers a
  1371.                             reload }
  1372.                Offset : LongInt;  { The offset to the segment in the .OVR file }
  1373.                Code_Bytes,        { Size of the code image }
  1374.                Reloc_Bytes,       { Number of relocation fixups times 2 }
  1375.                Entry_Count,       { The number of entry points }
  1376.                NextSeg,           { Next overlay segment - add prefixseg + $10 to find
  1377.                             thunks.  List starts with System.ovrcodelist. }
  1378.                LoadSeg,           { The segment at which the overlay is loaded, or 0 }
  1379.                Reprieve,          { Set to 1 to if overlay used while on probation }
  1380.                NextLoaded : Word; { The segment of the next loaded overlay.  List starts
  1381.                             with System.ovrloadlist.  Updated *after* call to
  1382.                             ovrreadbuf. }
  1383.                case Integer of
  1384.                  1 : (EMSPage,    { The EMS page where this overlay is stored }
  1385.                       EMSOffset : Word); { The offset within the EMS page }
  1386.                  2 : (S : PStream; { The stream holding this segment's code }
  1387.                       Soffset : LongInt); { The offset within S }
  1388.              end;
  1389.  
  1390. var
  1391.   OldReadFunc : OvrReadFunc;
  1392.   OvrOldExitProc : Pointer;
  1393.   OvrStream : PStream;
  1394. const
  1395.   OvrStreamInstalled : Boolean = False;
  1396.   OvrExitHandler : Boolean = False;
  1397.  
  1398.   function OvrPtr(Seg : Word) : POvrhead;
  1399. { Convert map style segment number, as used by overlay manager, to
  1400.   pointer }
  1401.   begin
  1402.     OvrPtr := Ptr(Seg+PrefixSeg+$10, 0);
  1403.   end;
  1404.  
  1405.   function StdPtr(Seg : Word) : POvrhead;
  1406.     { Convert straight segment number to a pointer }
  1407.   begin
  1408.     StdPtr := Ptr(Seg, 0);
  1409.   end;
  1410.  
  1411.   function NewReadFunc(OvrSeg : Word) : Integer; Far;
  1412.   var
  1413.     Result : Integer;
  1414.   begin
  1415.     with StdPtr(OvrSeg)^ do
  1416.     begin
  1417.       if S = nil then
  1418.       begin                       { Segment not yet loaded }
  1419.         Result := OldReadFunc(OvrSeg);
  1420.         if Result = 0 then
  1421.         begin
  1422.           { Now copy the loaded code to our stream }
  1423.           Soffset := OvrStream^.GetSize;
  1424.           OvrStream^.Seek(Soffset);
  1425.           OvrStream^.Write(Ptr(LoadSeg, 0)^, Code_Bytes);
  1426.           Result := OvrStream^.Status;
  1427.           if Result = stOK then
  1428.             S := OvrStream
  1429.           else
  1430.             OvrStream^.Reset;     { Something failed; hope we haven't messed
  1431.                               up the stream too much }
  1432.         end;
  1433.       end
  1434.       else
  1435.       begin                       { Segment has been loaded into the stream }
  1436.         S^.Seek(Soffset);
  1437.         S^.Read(Ptr(LoadSeg, 0)^, Code_Bytes);
  1438.         Result := S^.Status;
  1439.         if Result <> stOK then
  1440.         begin
  1441.           S^.Reset;               { Fix the stream, and try a standard load }
  1442.           Result := OldReadFunc(OvrSeg);
  1443.         end;
  1444.       end;
  1445.     end;
  1446.     NewReadFunc := Result;
  1447.   end;
  1448.  
  1449.   procedure OvrExitProc; Far;
  1450. { Installed exit procedure; disposes of any streams that are still
  1451.   handling overlays. }
  1452.   begin
  1453.     ExitProc := OvrOldExitProc;
  1454.     OvrDisposeStreams;
  1455.   end;
  1456.  
  1457.   procedure OvrInitStream(S : PStream);
  1458.   begin
  1459.     if not OvrStreamInstalled then
  1460.     begin
  1461.       OldReadFunc := OvrReadBuf;  { Install our reader function }
  1462.       OvrReadBuf := NewReadFunc;
  1463.       OvrStreamInstalled := True;
  1464.     end;
  1465.     if not OvrExitHandler then
  1466.     begin
  1467.       OvrOldExitProc := ExitProc;
  1468.       ExitProc := @OvrExitProc;
  1469.       OvrExitHandler := True;
  1470.     end;
  1471.     OvrStream := S;               { And set stream to use }
  1472.   end;
  1473.  
  1474.   procedure OvrDetachStream(BadS : PStream);
  1475.   var
  1476.     OvrSeg : Word;
  1477.   begin
  1478.     if OvrStreamInstalled then
  1479.     begin
  1480.       if OvrStream = BadS then
  1481.         OvrStream := nil;         { Detach default stream }
  1482.       OvrSeg := OvrCodeList;
  1483.       while OvrSeg <> 0 do        { Walk the overlay list }
  1484.         with OvrPtr(OvrSeg)^ do
  1485.         begin
  1486.           if S <> nil then
  1487.           begin
  1488.             if S <> BadS then
  1489.             begin
  1490.               if OvrStream = nil then
  1491.                 OvrStream := S;   { Set default stream to first found }
  1492.             end
  1493.             else
  1494.               S := nil;           { Blank out BadS references }
  1495.           end;
  1496.           OvrSeg := NextSeg;
  1497.         end;
  1498.       if OvrStream = nil then
  1499.       begin
  1500.         OvrStreamInstalled := False; { If we don't have a stream, better
  1501.                                           uninstall. }
  1502.         OvrReadBuf := OldReadFunc;
  1503.       end;
  1504.     end;
  1505.   end;
  1506.  
  1507.   procedure OvrDisposeStreams;
  1508.   var
  1509.     S : PStream;
  1510.   begin
  1511.     while OvrStreamInstalled and (OvrStream <> nil) do
  1512.     begin
  1513.       S := OvrStream;
  1514.       OvrDetachStream(S);
  1515.       Dispose(S, Done);
  1516.     end;
  1517.   end;
  1518.  
  1519.   function OvrSizeNeeded : LongInt;
  1520.   var
  1521.     OvrSeg : Word;
  1522.     Result : LongInt;
  1523.   begin
  1524.     OvrSeg := OvrCodeList;
  1525.     Result := 0;
  1526.     while OvrSeg <> 0 do          { Walk the overlay list }
  1527.       with OvrPtr(OvrSeg)^ do
  1528.       begin
  1529.         if S = nil then
  1530.           Inc(Result, Code_Bytes);
  1531.         OvrSeg := NextSeg;
  1532.       end;
  1533.     OvrSizeNeeded := Result;
  1534.   end;
  1535.  
  1536.   function OvrLoadAll : Boolean;
  1537.   var
  1538.     OvrSeg : Word;
  1539.     Junk : Integer;
  1540.   begin
  1541.     if not OvrStreamInstalled then
  1542.       OvrLoadAll := False
  1543.     else
  1544.     begin
  1545.       OvrClearBuf;
  1546.       OvrSeg := OvrCodeList;
  1547.       while OvrSeg <> 0 do        { Walk the overlay list }
  1548.         with OvrPtr(OvrSeg)^ do
  1549.         begin
  1550.           if S = nil then
  1551.           begin
  1552.             LoadSeg := OvrHeapOrg; { load at start of overlay buffer }
  1553.             Junk := NewReadFunc(OvrSeg+PrefixSeg+$10);
  1554.             LoadSeg := 0;         { Don't really want it loaded yet }
  1555.           end;
  1556.           OvrSeg := NextSeg;
  1557.         end;
  1558.       OvrLoadAll := OvrStream^.Status = stOK;
  1559.     end;
  1560.   end;
  1561.  
  1562.   {$endif windows}
  1563.  
  1564.   { ****** Bit filter code ****** }
  1565.  
  1566.   constructor TBitFilter.Init(ABase : PStream);
  1567.   begin
  1568.     TFilter.Init(ABase);
  1569.     BitPos := 0;
  1570.     AtEnd := false;
  1571.   end;
  1572.  
  1573.   procedure TBitFilter.PrepareBuffer(ForRead : Boolean);
  1574.   begin
  1575.     if BitPos = 8 then            { Buffer full on write }
  1576.     begin
  1577.       Base^.Write(Buffer, 1);
  1578.       BitPos := 0;
  1579.     end;
  1580.     if BitPos = 0 then            { Buffer empty }
  1581.     begin
  1582.       if not AtEnd then
  1583.       begin
  1584.         if not ForRead then
  1585.           AtEnd := (Base^.GetPos >= Base^.GetSize);
  1586.         if (not AtEnd) or ForRead then
  1587.         begin
  1588.           Base^.Read(Buffer,1);
  1589.           BitPos := -8
  1590.         end;
  1591.       end;
  1592.       if AtEnd then
  1593.         Buffer := 0;
  1594.       Mask := 1;
  1595.     end;
  1596.     if (not ForRead) and (BitPos < 0) then
  1597.     begin
  1598.       Base^.Seek(Base^.GetPos-1);
  1599.       Inc(BitPos, 8);
  1600.       AtEnd := false;
  1601.     end;
  1602.   end;
  1603.  
  1604.   function TBitFilter.GetBit : TBit;
  1605.   begin
  1606.     if CheckStatus then
  1607.     begin
  1608.       PrepareBuffer(True);
  1609.       GetBit := TBit((Buffer and Mask) > 0);
  1610.       Mask := Mask shl 1;
  1611.       Inc(BitPos);
  1612.       CheckBase;
  1613.     end;
  1614.   end;
  1615.  
  1616.   function TBitFilter.GetBits(Count : Byte) : LongInt;
  1617.   var
  1618.     Result : LongInt;
  1619.   begin
  1620.     Result := 0;
  1621.     ReadBits(Result, Count);
  1622.     GetBits := Result;
  1623.   end;
  1624.  
  1625.   procedure TBitFilter.PutBit(ABit : TBit);
  1626.   begin
  1627.     if CheckStatus then
  1628.     begin
  1629.       PrepareBuffer(False);
  1630.       if ABit = 1 then
  1631.         Buffer := Buffer or Mask;
  1632.       Mask := Mask shl 1;
  1633.       Inc(BitPos);
  1634.     end;
  1635.   end;
  1636.  
  1637.   procedure TBitFilter.PutBits(ABits : LongInt; Count : Byte);
  1638.   begin
  1639.     WriteBits(ABits, Count);
  1640.   end;
  1641.  
  1642.   procedure TBitFilter.ReadBits(var Buf; Count : LongInt);
  1643.   var
  1644.     w : Word;
  1645.     b : array[1..2] of Byte absolute w;
  1646.     bBuf : TByteArray absolute Buf;
  1647.     i, Bytes : Word;
  1648.     Shift : Word;
  1649.   begin
  1650.     if (Count > 0) and CheckStatus then
  1651.     begin
  1652.       PrepareBuffer(True);
  1653.       if BitPos > 0 then
  1654.       begin
  1655.         Base^.Write(Buffer, 1);
  1656.         Dec(BitPos, 8);
  1657.       end;
  1658.       Shift := BitPos+8;          { the number of bits to shift by }
  1659.       Bytes := (Count+Shift-1) div 8; { Count of whole bytes to read }
  1660.       if Bytes > 0 then
  1661.       begin
  1662.         TFilter.Read(Buf, Bytes);
  1663.         b[1] := Buffer;
  1664.         for i := 0 to Pred(Bytes) do
  1665.         begin
  1666.           b[2] := bBuf[i];
  1667.           w := w shr Shift;
  1668.           bBuf[i] := b[1];
  1669.           w := w shr (8-Shift);
  1670.         end;
  1671.         Buffer := b[1];
  1672.       end;
  1673.       { Now fix up the last few bits }
  1674.       Dec(Count, 8*LongInt(Bytes));
  1675.       if Count > 0 then
  1676.         bBuf[Bytes] := (Buffer shr Shift) and not($FF shl Count)
  1677.       else
  1678.         if Count < 0 then
  1679.           bBuf[Bytes-1] := bBuf[Bytes-1] and not($FF shl (8+Count));
  1680.       BitPos := BitPos+Count;
  1681.       Mask := 1 shl (BitPos+8);
  1682.     end;
  1683.   end;
  1684.  
  1685.   procedure TBitFilter.WriteBits(var Buf; Count : LongInt);
  1686.   var
  1687.     w : Word;
  1688.     b : array[1..2] of Byte absolute w;
  1689.     bBuf : TByteArray absolute Buf;
  1690.     i, Bytes : Word;
  1691.     Shift : Word;
  1692.     SaveBuf : Byte;
  1693.     SavePos : ShortInt;
  1694.   begin
  1695.     if CheckStatus then
  1696.     begin
  1697.       PrepareBuffer(False);
  1698.       Bytes := (Count+BitPos-1) div 8; { Count of whole bytes to write }
  1699.       Shift := 8-BitPos;
  1700.       if Bytes > 0 then
  1701.       begin
  1702.         if Shift < 8 then
  1703.         begin
  1704.           b[1] := Buffer shl Shift;
  1705.           for i := 0 to Pred(Bytes) do
  1706.           begin
  1707.             b[2] := bBuf[i];
  1708.             w := w shr Shift;
  1709.             Base^.Write(b[1], 1);
  1710.             w := w shr (8-Shift);
  1711.           end;
  1712.           Buffer := b[1] shr Shift;
  1713.         end
  1714.         else
  1715.           Base^.Write(Buf, Bytes);
  1716.       end;
  1717.       Dec(Count, 8*LongInt(Bytes));
  1718.       if Count > 0 then
  1719.         Buffer := (Buffer or (bBuf[Bytes] shl (8-Shift)));
  1720.       BitPos := BitPos+Count;
  1721.       if BitPos > 0 then          { Fill in upper part of buffer }
  1722.       begin
  1723.         SaveBuf := Buffer;
  1724.         SavePos := BitPos;
  1725.         BitPos := 0;              { signal empty buffer }
  1726.         PrepareBuffer(False);     { and load it }
  1727.         Buffer := (Buffer and ($FF shl SavePos)) { old part }
  1728.                   or (SaveBuf and not($FF shl SavePos)); { new part }
  1729.         BitPos := SavePos;
  1730.       end;
  1731.       Mask := 1 shl BitPos;
  1732.       CheckBase;
  1733.     end;
  1734.   end;
  1735.  
  1736.   procedure TBitFilter.Flush;
  1737.   begin
  1738.     if CheckStatus then
  1739.     begin
  1740.       if BitPos > 0 then
  1741.         Base^.Write(Buffer, 1);
  1742.       Dec(BitPos, 8);
  1743.       AtEnd := false;
  1744.       CheckBase;
  1745.     end;
  1746.   end;
  1747.  
  1748.   procedure TBitFilter.Seek(Pos : LongInt);
  1749.   begin
  1750.     if CheckStatus then
  1751.     begin
  1752.       Flush;
  1753.       TFilter.Seek(Pos);
  1754.       BitPos := 0;
  1755.       AtEnd := false;
  1756.     end;
  1757.   end;
  1758.  
  1759.   procedure TBitFilter.Read(var Buf; Count : Word);
  1760.   begin
  1761.     ReadBits(Buf, 8*LongInt(Count));
  1762.   end;
  1763.  
  1764.   procedure TBitFilter.Write(var Buf; Count : Word);
  1765.   begin
  1766.     WriteBits(Buf, 8*LongInt(Count));
  1767.   end;
  1768.  
  1769.   procedure TBitFilter.SeekBit(Pos : LongInt);
  1770.   var
  1771.     i : Byte;
  1772.     b : TBit;
  1773.   begin
  1774.     if CheckStatus then
  1775.     begin
  1776.       Seek(Pos div 8);
  1777.       for i := 1 to (Pos and 7) do
  1778.         b := GetBit;
  1779.     end;
  1780.   end;
  1781.  
  1782.   function TBitFilter.GetBitPos : LongInt;
  1783.   begin
  1784.     GetBitPos := 8*TFilter.GetPos+BitPos;  { Need TFilter override in
  1785.                                              case descendants override
  1786.                                              GetPos }
  1787.   end;
  1788.  
  1789.   procedure TBitFilter.CopyBits(var S : TBitFilter; Count : LongInt);
  1790.   var
  1791.     localbuf : array[1..256] of Byte;
  1792.   begin
  1793.     while Count > 2048 do
  1794.     begin
  1795.       S.ReadBits(localbuf, 2048);
  1796.       WriteBits(localbuf, 2048);
  1797.       Dec(Count, 2048);
  1798.     end;
  1799.     if Count > 0 then
  1800.     begin
  1801.       S.ReadBits(localbuf, Count);
  1802.       WriteBits(localbuf, Count);
  1803.     end;
  1804.   end;
  1805.  
  1806.   procedure TBitFilter.ByteAlign;
  1807.   begin
  1808.     SeekBit((GetBitPos+7) and $FFFFFFF8);
  1809.   end;
  1810.  
  1811.   { ****** Duplicate filter code ****** }
  1812.  
  1813.   constructor TDupFilter.Init(ABase, ABase2 : PStream);
  1814.   { Initialize the filter with the given bases. }
  1815.   begin
  1816.     if not TFilter.Init(Abase) then
  1817.       fail;
  1818.     Base2 := ABase2;
  1819.     CheckBase2;
  1820.     if Status = stOK then
  1821.       Startofs2 := Base2^.GetPos;
  1822.   end;
  1823.  
  1824.   destructor TDupFilter.Done;
  1825.   { Flush filter, then dispose of both bases. }
  1826.   begin
  1827.     Flush;
  1828.     if Base2 <> nil then
  1829.       Dispose(Base2,done);
  1830.     TFilter.Done;
  1831.   end;
  1832.  
  1833.   function TDupFilter.MisMatch(var buf1,buf2;count:word):word;
  1834.   var
  1835.     i : word;
  1836.     bbuf1 : TByteArray absolute buf1;
  1837.     bbuf2 : TByteArray absolute buf2;
  1838.   begin
  1839.     for i := 0 to pred(count) do
  1840.       if bbuf1[i] <> bbuf2[i] then
  1841.       begin
  1842.         MisMatch := succ(i);
  1843.         exit;
  1844.       end;
  1845.     MisMatch := 0;
  1846.   end;
  1847.  
  1848.   procedure TDupFilter.Read(var Buf; Count : Word);
  1849.   var
  1850.     bpos : word;
  1851.     localbuf : array[0..255] of byte;
  1852.  
  1853.     procedure CompareBuffer(size:word);
  1854.     var
  1855.       epos : word;
  1856.       bbuf : TByteArray absolute Buf;
  1857.     begin
  1858.       Base2^.Read(localbuf,size);
  1859.       dec(count,size);
  1860.       CheckBase2;
  1861.       if status = stOK then
  1862.       begin
  1863.         epos := MisMatch(bbuf[bpos],localbuf,size);
  1864.         if epos <> 0 then
  1865.           Error(stMismatch,bpos+epos);
  1866.       end;
  1867.       inc(bpos,size);
  1868.     end;
  1869.  
  1870.   begin
  1871.     TFilter.Read(buf, Count);
  1872.     bpos := 0;
  1873.     While (Status = stOK) and (Count >= sizeof(localbuf)) do
  1874.       CompareBuffer(Sizeof(localbuf));
  1875.     If (Status = stOK) and (Count > 0) then
  1876.       CompareBuffer(Count);
  1877.     { Be sure the bases are synchronized }
  1878.     Base2^.Seek(GetPos+StartOfs2);
  1879.   end;
  1880.  
  1881.   procedure TDupFilter.Seek(Pos : LongInt);
  1882.   begin
  1883.     TFilter.Seek(Pos);
  1884.     if Status = stOK then
  1885.     begin
  1886.       base2^.Seek(pos+startofs2);
  1887.       CheckBase2;
  1888.     end;
  1889.   end;
  1890.  
  1891.   procedure TDupFilter.Truncate;
  1892.   begin
  1893.     TFilter.Truncate;
  1894.     if Status = stOK then
  1895.     begin
  1896.       base2^.truncate;
  1897.       CheckBase2;
  1898.     end;
  1899.   end;
  1900.  
  1901.   procedure TDupFilter.Write(var Buf; Count : Word);
  1902.   begin
  1903.     TFilter.Write(buf,Count);
  1904.     if Status = stOK then
  1905.     begin
  1906.       Base2^.write(buf,Count);
  1907.       CheckBase2;
  1908.     end;
  1909.   end;
  1910.  
  1911.   procedure TDupFilter.Flush;
  1912.   begin
  1913.     TFilter.Flush;
  1914.     if Status = stOK then
  1915.     begin
  1916.       base2^.flush;
  1917.       CheckBase2;
  1918.     end;
  1919.   end;
  1920.  
  1921.   function TDupFilter.CheckStatus : Boolean;
  1922.   begin
  1923.     if TFilter.CheckStatus then
  1924.       if Base2^.Status <> stOK then
  1925.         Base2^.Reset;
  1926.     CheckStatus := Status = stOK;
  1927.   end;
  1928.  
  1929.   procedure TDupFilter.CheckBase2;
  1930.   begin
  1931.     if Base2^.status <> stOk then
  1932.       Error(stBase2Error,Base2^.status);
  1933.   end;
  1934.  
  1935.   { ****** Checksum/CRC code ******}
  1936.  
  1937.   Function UpdateChksum(initsum:word; var Inbuf; inlen:word):word;
  1938.   var
  1939.     i : word;
  1940.     bbuf : TByteArray absolute inbuf;
  1941.   begin
  1942.     for i:=0 to pred(inlen) do
  1943.       inc(initsum,bbuf[i]);
  1944.     UpdateChksum := initsum;
  1945.   end;
  1946.  
  1947. { From the original CRC.PAS: }
  1948.  
  1949. { This unit provides three speed-optimized functions to compute (or continue
  1950.   computation of) a Cyclic Redundency Check (CRC).  These routines are
  1951.   contributed to the public domain (with the limitations noted by the
  1952.   original authors in the TASM sources).
  1953.  
  1954.   Each function takes three parameters:
  1955.  
  1956.   InitCRC - The initial CRC value.  This may be the recommended initialization
  1957.   value if this is the first or only block to be checked, or this may be
  1958.   a previously computed CRC value if this is a continuation.
  1959.  
  1960.   InBuf - An untyped parameter specifying the beginning of the memory area
  1961.   to be checked.
  1962.  
  1963.   InLen - A word indicating the length of the memory area to be checked.  If
  1964.   InLen is zero, the function returns the value of InitCRC.
  1965.  
  1966.   The function result is the updated CRC.  The input buffer is scanned under
  1967.   the limitations of the 8086 segmented architecture, so the result will be
  1968.   in error if InLen > 64k - Offset(InBuf).
  1969.  
  1970.   These conversions were done on 10-29-89 by:
  1971.  
  1972.   Edwin T. Floyd [76067,747]
  1973.   #9 Adams Park Court
  1974.   Columbus, GA 31909
  1975.   (404) 576-3305 (work)
  1976.   (404) 322-0076 (home)
  1977. }
  1978.  
  1979. Function UpdateCRC16(InitCRC : Word; Var InBuf; InLen : Word) : Word;
  1980.   external; {$L crc16.obj}
  1981. { I believe this is the CRC used by the XModem protocol.  The transmitting
  1982.   end should initialize with zero, UpdateCRC16 for the block, Continue the
  1983.   UpdateCRC16 for two nulls, and append the result (hi order byte first) to
  1984.   the transmitted block.  The receiver should initialize with zero and
  1985.   UpdateCRC16 for the received block including the two byte CRC.  The
  1986.   result will be zero (why?) if there were no transmission errors.  (I have
  1987.   not tested this function with an actual XModem implementation, though I
  1988.   did verify the behavior just described.  See TESTCRC.PAS.) }
  1989.  
  1990.  
  1991. Function UpdateCRCArc(InitCRC : Word; Var InBuf; InLen : Word) : Word;
  1992.   external; {$L crcarc.obj}
  1993. { This function computes the CRC used by SEA's ARC utility.  Initialize
  1994.   with zero. }
  1995.  
  1996. Function UpdateCRC32(InitCRC : LongInt; Var InBuf; InLen : Word) : LongInt;
  1997.   external; {$L crc32.obj}
  1998. { This function computes the CRC used by PKZIP and Forsberg's ZModem.
  1999.   Initialize with high-values ($FFFFFFFF), and finish by inverting all bits
  2000.   (Not). }
  2001.  
  2002.   { ****** Sequential filter code ****** }
  2003.  
  2004.   procedure TSequential.Seek(pos:longint);
  2005.   begin
  2006.     Error(stUnsupported,0);
  2007.   end;
  2008.  
  2009.   { ****** Chksum filter code ******}
  2010.  
  2011.   constructor TChkSumFilter.init(ABase:PStream; AChksum:word);
  2012.   begin
  2013.     if not TSequential.init(ABase) then
  2014.       fail;
  2015.     Chksum := AChksum;
  2016.   end;
  2017.  
  2018.   procedure TChkSumFilter.Read(var buf; Count:word);
  2019.   begin
  2020.     TSequential.Read(buf,count);
  2021.     if status = stOK then
  2022.       ChkSum := UpdateChksum(ChkSum,buf,Count);
  2023.   end;
  2024.  
  2025.   procedure TChkSumFilter.Write(var buf; Count:word);
  2026.   begin
  2027.     TSequential.Write(buf,count);
  2028.     if status = stOk then
  2029.       ChkSum := UpdateChksum(ChkSum,buf,Count);
  2030.   end;
  2031.  
  2032. { ***** CRC16 filter code ***** }
  2033.  
  2034.   constructor TCRC16Filter.init(ABase:PStream; ACRC16:word);
  2035.   begin
  2036.     if not TSequential.init(ABase) then
  2037.       fail;
  2038.     CRC16 := ACRC16;
  2039.   end;
  2040.  
  2041.   procedure TCRC16Filter.Read(var buf; Count:word);
  2042.   begin
  2043.     TSequential.Read(buf,count);
  2044.     if status = stOK then
  2045.       CRC16 := UpdateCRC16(CRC16,buf,count);
  2046.   end;
  2047.  
  2048.   procedure TCRC16Filter.Write(var buf; Count:word);
  2049.   begin
  2050.     TSequential.Write(buf,count);
  2051.     if status = stOk then
  2052.       CRC16 := UpdateCRC16(CRC16,buf,count);
  2053.   end;
  2054.  
  2055.   { ***** CRCARC filter code ***** }
  2056.  
  2057.   constructor TCRCARCFilter.init(ABase:PStream; ACRCARC:word);
  2058.   begin
  2059.     if not TSequential.init(ABase) then
  2060.       fail;
  2061.     CRCARC := ACRCARC;
  2062.   end;
  2063.  
  2064.   procedure TCRCARCFilter.Read(var buf; Count:word);
  2065.   begin
  2066.     TSequential.Read(buf,count);
  2067.     if status = stOK then
  2068.       CRCARC := UpdateCRCARC(CRCARC,buf,count);
  2069.   end;
  2070.  
  2071.   procedure TCRCARCFilter.Write(var buf; Count:word);
  2072.   begin
  2073.     TSequential.Write(buf,count);
  2074.     if status = stOk then
  2075.       CRCARC := UpdateCRCARC(CRCARC,buf,count);
  2076.   end;
  2077.  
  2078.   { ***** CRC32 filter code ***** }
  2079.  
  2080.   constructor TCRC32Filter.init(ABase:PStream; ACRC32:longint);
  2081.   begin
  2082.     if not TSequential.init(ABase) then
  2083.       fail;
  2084.     CRC32 := ACRC32;
  2085.   end;
  2086.  
  2087.   procedure TCRC32Filter.Read(var buf; Count:word);
  2088.   begin
  2089.     TSequential.Read(buf,count);
  2090.     if status = stOK then
  2091.       CRC32 := UpdateCRC32(CRC32,buf,count);
  2092.   end;
  2093.  
  2094.   procedure TCRC32Filter.Write(var buf; Count:word);
  2095.   begin
  2096.     TSequential.Write(buf,count);
  2097.     if status = stOk then
  2098.       CRC32 := UpdateCRC32(CRC32,buf,count);
  2099.   end;
  2100.  
  2101.   { ****** Null stream code ****** }
  2102.  
  2103.   constructor TNulStream.Init;
  2104.   begin
  2105.     TStream.Init;
  2106.     Position := 0;
  2107.     Value := AValue;
  2108.   end;
  2109.  
  2110.   function TNulStream.GetPos;
  2111.   begin
  2112.     GetPos := Position;
  2113.   end;
  2114.  
  2115.   function TNulStream.GetSize;
  2116.   begin
  2117.     GetSize := Position;
  2118.   end;
  2119.  
  2120.   procedure TNulStream.Read;
  2121.   begin
  2122.     FillChar(Buf, Count, Value);
  2123.     Inc(Position, Count);
  2124.   end;
  2125.  
  2126.   procedure TNulStream.Seek;
  2127.   begin
  2128.     Position := Pos;
  2129.   end;
  2130.  
  2131.   procedure TNulStream.Write;
  2132.   begin
  2133.     Inc(Position, Count);
  2134.   end;
  2135.  
  2136.   { ****** RAM stream code ****** }
  2137.  
  2138.   constructor TRAMStream.Init(Asize : Word);
  2139.   begin
  2140.     TStream.Init;
  2141.     Position := 0;
  2142.     Size := 0;
  2143.     Alloc := Asize;
  2144.     if MaxAvail < Alloc then
  2145.       Fail;
  2146.     GetMem(Buffer, Alloc);
  2147.     OwnMem := True;
  2148.     FillChar(Buffer^, Alloc, 0);
  2149.   end;
  2150.  
  2151.   constructor TRAMStream.UseBuf(ABuffer : Pointer; Asize : Word);
  2152.   begin
  2153.     TRAMStream.Init(0);
  2154.     Alloc := Asize;
  2155.     Buffer := ABuffer;
  2156.     OwnMem := False;
  2157.   end;
  2158.  
  2159.   destructor TRAMStream.Done;
  2160.   begin
  2161.     if OwnMem then
  2162.       FreeMem(Buffer, Alloc);
  2163.     TStream.Done;
  2164.   end;
  2165.  
  2166.   function TRAMStream.GetPos;
  2167.   begin
  2168.     GetPos := Position;
  2169.   end;
  2170.  
  2171.   function TRAMStream.GetSize;
  2172.   begin
  2173.     GetSize := Size;
  2174.   end;
  2175.  
  2176.   procedure TRAMStream.Read;
  2177.   begin
  2178.     if Position+Count > Size then
  2179.     begin
  2180.       Error(stReaderror, 0);
  2181.       FillChar(Buf, Count, 0);
  2182.     end
  2183.     else
  2184.     begin
  2185.       Move(Buffer^[Position], Buf, Count);
  2186.       Inc(Position, Count);
  2187.     end;
  2188.   end;
  2189.  
  2190.   procedure TRAMStream.Seek;
  2191.   begin
  2192.     if Pos > Size then
  2193.       Error(stReaderror, 0)
  2194.     else
  2195.       Position := Pos;
  2196.   end;
  2197.  
  2198.   procedure TRAMStream.Truncate;
  2199.   begin
  2200.     Size := Position;
  2201.   end;
  2202.  
  2203.   procedure TRAMStream.Write;
  2204.   begin
  2205.     if Position+Count > Alloc then
  2206.       Error(stWriteError, 0)
  2207.     else
  2208.     begin
  2209.       Move(Buf, Buffer^[Position], Count);
  2210.       Inc(Position, Count);
  2211.       if Position > Size then
  2212.         Size := Position;
  2213.     end;
  2214.   end;
  2215.  
  2216.   { ***** XMS stream code ***** }
  2217.  
  2218.   {$I xmsstrm.inc}
  2219.  
  2220.   { ***** Named Buffered file stream code ***** }
  2221.  
  2222.   constructor TNamedBufStream.Init(Name : FNameStr; Mode : TOpenMode; ABufSize : Word);
  2223.   begin
  2224.     if TBufStream.Init(Name, Mode, ABufSize) then
  2225.     {$ifdef windows}
  2226.     filename := StrNew(name)
  2227.     {$else}
  2228.       Filename := NewStr(Name)
  2229.     {$endif}
  2230.     else
  2231.       Fail;
  2232.   end;
  2233.  
  2234.   destructor TNamedBufStream.Done;
  2235.   begin
  2236.   {$ifdef windows}
  2237.   StrDispose(filename);
  2238.   {$else}
  2239.     DisposeStr(Filename);
  2240.   {$endif}
  2241.     TBufStream.Done;
  2242.   end;
  2243.  
  2244.   constructor TTempBufStream.Init(ABufSize : Word);
  2245.   var
  2246.     p : Pchar;
  2247.     TempName : String;
  2248.     Okay : Boolean;
  2249.     NewHandle : Word;
  2250.   begin
  2251.     if not TStream.Init then
  2252.       Fail;
  2253.     if MaxAvail < ABufSize then
  2254.       Fail;
  2255.     BufSize := ABufSize;
  2256.     GetMem(Buffer, BufSize);
  2257.  
  2258.   {$ifdef windows}
  2259.   p := GetEnvVar('TEMP');
  2260.   if p <> nil then
  2261.     tempname := StrPas(p)
  2262.   else
  2263.     tempname := '';
  2264.   {$else}
  2265.     TempName := GetEnv('TEMP');
  2266.   {$endif}
  2267.     if Length(TempName) = 0 then
  2268.       TempName := '.\';
  2269.     if TempName[Length(TempName)] <> '\' then
  2270.       TempName := TempName+'\';
  2271.     FillChar(TempName[Length(TempName)+1], 255-Length(TempName), #0);
  2272.     asm
  2273.       push    ds
  2274.       push    ss
  2275.       pop     ds
  2276.       lea     dx,TempName[1]
  2277.       mov     ah, $5a
  2278.       xor     cx,cx
  2279.     {$ifdef windows}
  2280.     call dos3call
  2281.     {$else}
  2282.       int     $21                 { Create temporary file. }
  2283.     {$endif}
  2284.       pop     ds
  2285.       jc      @failed
  2286.       mov     Okay,True
  2287.       mov     NewHandle,ax
  2288.       jmp     @done
  2289. @failed:
  2290.       mov     Okay,False
  2291. @done:
  2292.     end;
  2293.     if not Okay then
  2294.       Fail;
  2295.     Handle := NewHandle;
  2296.     while TempName[Length(TempName)+1] <> #0 do
  2297.       Inc(TempName[0]);
  2298.   {$ifdef windows}
  2299.   filename := StrNew(StrPCopy(@tempname,tempname));
  2300.   {$else}
  2301.     Filename := NewStr(TempName);
  2302.   {$endif}
  2303.   end;
  2304.  
  2305.   destructor TTempBufStream.Done;
  2306.   var
  2307.     F : file;
  2308.   begin
  2309.   {$ifdef windows}
  2310.   assign(f,StrPas(Filename));
  2311.   {$else}
  2312.     Assign(F, Filename^);
  2313.   {$endif}
  2314.     TNamedBufStream.Done;
  2315.     Erase(F);
  2316.   end;
  2317.  
  2318.   { ***** Temp Stream Code ******* }
  2319.  
  2320.   function TempStream(InitSize, MaxSize : LongInt;
  2321.                       Preference : TStreamRanking) : PStream;
  2322.   var
  2323.     Choice : Integer;
  2324.     Result : PStream;
  2325.     StreamType : TStreamType;
  2326.     Nulls : TNulStream;
  2327.   begin
  2328.     Result := nil;
  2329.     Nulls.Init(0);
  2330.     for Choice := 1 to NumTypes do
  2331.     begin
  2332.       StreamType := Preference[Choice];
  2333.       case StreamType of
  2334.         RAMStream :
  2335.           if MaxSize < $10000 then
  2336.             Result := New(PRAMStream, Init(MaxSize));
  2337.         EMSStream :
  2338.           Result := New(PEMSStream, Init(InitSize, MaxSize));
  2339.         XMSStream :
  2340.           if xms_MaxAvail > MaxSize div xms_BlockSize then
  2341.             Result := New(PXMSStream, Init(MaxSize div xms_BlockSize+1));
  2342.         FileStream :
  2343.           Result := New(PTempBufStream, Init(2048));
  2344.       end;
  2345.       if (Result <> nil) and (Result^.Status = stOK) then
  2346.       begin
  2347.         Result^.Copyfrom(Nulls, InitSize);
  2348.         Result^.Seek(0);
  2349.         if Result^.Status = stOK then
  2350.         begin
  2351.           Nulls.Done;
  2352.           TempStream := Result;
  2353.           Exit;
  2354.         end;
  2355.       end;
  2356.       if Result <> nil then
  2357.         Dispose(Result, Done); { Clean up and start over } ;
  2358.       Result := nil;
  2359.     end;
  2360.     TempStream := nil;
  2361.   end;
  2362. end.
  2363.